package spring.mvc;

import java.time.LocalDateTime;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;

import com.me.common.util.SleepUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.AsyncRestTemplate;
import org.springframework.web.context.request.async.DeferredResult;
import org.springframework.web.context.request.async.WebAsyncTask;

/**
 * spring 异步接口测试类
 *
 * @author ME
 * @date   2020/1/9
 */
@RestController
@RequestMapping("/test")
public class DeferredResultController {
    private static final Logger log = LoggerFactory.getLogger(DeferredResultController.class);

    private final Map<String, DeferredResult<String>> map = new ConcurrentHashMap<>();

    /**
     * 异步接口：返回 DeferredResult
     *  http://127.0.0.1:8080/test/deferredResult
     */
    @RequestMapping(value = "/deferredResult")
    @ResponseBody
    public DeferredResult<String> deferred(){
        // 这里的 3000 L ，是指主线程结束后的超时时间。
        DeferredResult<String> result = new DeferredResult<>(3000L , "执行超时了");
        new Thread(() -> {
            try {
                Thread.sleep(5 * 1000);
                result.setResult("同意: " + LocalDateTime.now());
            } catch (InterruptedException e) {
                result.setErrorResult("执行出错啦");
            }
        }).start();
        // 主线程 等待执行线程 10s，之后返回并不会 timeout，实际不会这么用
        // SleepUtil.ms(10 * 1000);
        return result;
    }

    /**
     * 异步接口：返回 DeferredResult
     *  http://127.0.0.1:8080/test/deferredResult2
     *
     * @return
     */
    @RequestMapping(value = "/deferredResult2")
    @ResponseBody
    public DeferredResult<String> testDeferredResult(@RequestParam String param) {
        DeferredResult<String> deferredResult = new DeferredResult<>(3000L, "异步接口time out ~~");
        map.put("233", deferredResult);
        log.debug("异步接口 param：" + param);

        // deferredResult.onTimeout(() -> {});
        // deferredResult.onCompletion(() -> {});
        // deferredResult.onError(Throwable::printStackTrace);
        return deferredResult;
    }

    /**
     * 对 DeferredResult 的结果进行设置
     *  http://127.0.0.1:8080/test/setDeferredResult
     */
    @RequestMapping("/setDeferredResult")
    @ResponseBody
    public String setDeferredResult() {
        log.debug("I'm Async Response!");
        DeferredResult<String> deferredResult = map.get("233");
        Random random = new Random();
        if (random.nextBoolean()) {
            deferredResult.setResult("I'm Async Response!");
        } else {
            deferredResult.setErrorResult(new RuntimeException("I'm Error!"));
        }
        return "Async Response succeed";
    }

    /**
     * 异步接口：接口内部异步请求第三方接口时，返回 ListenableFuture
     *  http://127.0.0.1:8080/test/listenableFuture
     */
    @RequestMapping(value = "/listenableFuture")
    public ListenableFuture<ResponseEntity<String>> listenableFuture(){
        ListenableFuture<ResponseEntity<String>> future = new AsyncRestTemplate().getForEntity("http://www.baidu.com", String.class);
        return future;
    }


    /**
     * 异步接口：返回 WebAsyncTask
     *  http://127.0.0.1:8080/test/web
     */
    @RequestMapping(value = "/web")
    @ResponseBody
    public WebAsyncTask<String> web() {
        System.out.println("WebAsyncTask 进入主线程...");
        WebAsyncTask<String> task = new WebAsyncTask<>(() -> {
            Thread.sleep(5 * 1000);
            System.out.println("WebAsyncTask 子线程执行ing...");
            return "WebAsyncTask: 久等了";
        });
        System.out.println("WebAsyncTask 主线程退出...");
        return task;
    }

    /**
     * 异步接口：返回 Callable
     *  http://127.0.0.1:8080/test/callable
     */
    @RequestMapping("/callable")
    @ResponseBody
    public Callable<String> callable(final @RequestParam(required=false, defaultValue="true") boolean handled) {
        // 如果 Callable 中用到从页面参数，需要在参数前加入 final
        System.out.println("Callable进入主线程...");
        return () -> {
            if (handled) {
                Thread.sleep(2000);
            }
            Thread.sleep(2000);
            return "Callable result";
        };
    }

}
